<?php

    global $mikron;
    
    define('MAX_UNIXDATE', 2145906000); // 2038-01-01 00:00:00
    define('SECONDS_PER_DAY', 86400);

    class Mikron_Acl_Role
    {
        private $code;
        private $expired;
        public function __construct($code, $expired)
        {
            $this->code = $code;
            $this->expired = $expired;
        }
        /**
        * Get this role code value
        */
        public function getCode()
        {
            return $this->code;
        }
        /**
        * Get this role expired date value
        */
        public function getExpiredDate()
        {
            return $this->expired;
        }
    }

    class Mikron_Acl_Module
    {
        private $operations;
        function AddOperation($operation_code)
        {
            $this->operations[] = $operation_code;
        }
        function isAllowOperation($operation_code)
        {
            return in_array($operation_code, $this->operations);
        }
    }

    class Mikron_Acl_Operation
    {
    }

    class Mikron_Acl
    {
        public $roles = Array();
        private $allow = Array();
        private $deny = Array();
        private $mikron;
        private $modules = Array();

        /**
        * Метод защищающий от многократного выполнения методов имеющее
        * максимально допустимое количество вызовов в течении 1 сессии
        * 
        * @param mixed $operation_name
        * @param mixed $limit
        */
        public function IterateLimitedOperation($operation_name, $limit = 5)
        {
            global $mikron;
            $limited_operations = $mikron->Cache->getCache('limited_operations');
            if(!is_array($limited_operations))
            {
                $limited_operations = Array();
            }
            if(!array_key_exists($operation_name, $limited_operations))
            {
                $limited_operations[$operation_name] = Array();
            }
            $limited_operations[$operation_name][] = time();
            $mikron->Cache->setCache('limited_operations', $limited_operations);
            $run_count = count($limited_operations[$operation_name]);
            if($run_count >= $limit)
            {
                //$first_run_time = $limited_operations[$operation_name][$run_count];
                //if(time() - $first_run_time
                $mikron->Tools->ThrowPage(null, 503, '/');
            }
        }

        /**
        * Составление списка доступов
        */
        function Refresh()
        {
            $this->roles = Array();
            $this->allow = Array();
            $this->deny = Array();
            $this->modules = Array();
            if(is_null($user = $this->mikron->Users->CurrentUser()->user))
            {
                return true;
            }
            $users_roles = $this->mikron->Queries->Query('T_USERS_ROLES', null, new Criterion('user', $user->id));
            while($ur = $users_roles->fetch())
            {
                $role = $ur->role->value;
                $this->addRole(new Mikron_Acl_Role($role->code->value, $ur->expired->value));
                $role_operations = $this->mikron->Queries->Query('T_ROLES_OPERATIONS', null, new Criterion('role', $role->id));
                while($ro = $role_operations->fetch())
                {
                    $operation = $ro->operation->value;
                    $module = $operation->module->value;
                    $module_name = $module->name->value;
                    if(!array_key_exists($module_name, $this->modules))
                    {
                        $this->modules[$module_name] = new Mikron_Acl_Module();
                    }
                    $module = $this->modules[$module_name];
                    $module->AddOperation($operation->code->value);
                }
            }
        }

        function RoleExpiredString($role_code, $only_days = false)
        {
            global $mikron;
            $user = $mikron->Users->CurrentUser()->user;
            while($role = $user->roles->value->fetch())
            {
                if($role->code->value == $role_code)
                {
                    $dif = strtotime($roleitem->expired->value) - time();
                    if($dif > 0)
                    {
                        $dif_sec = $dif % 60;
                        $dif_min = floor($dif / 60) % 60;
                        $dif_hours = floor($dif / 3600) % 24;
                        $dif_days = floor($dif / 86400);
                        if($only_days)
                        {
                            return "$dif_days дн.";                            
                        }
                        else
                        {
                            return "$dif_days дн. $dif_hours ч. $dif_min мин. $dif_sec сек.";
                        }
                    }
                }
            }
            return null;
        }

        function hasRole($roleCode)
        {
            return array_key_exists($roleCode, $this->roles);
        }

        function isAllow($module_name, $operation_name)
        {
            if(array_key_exists($module_name, $this->modules))
            {
                return $this->modules[$module_name]->isAllowOperation($operation_name);
            }
            return false;
        }

        function __construct($mikron)
        {
            $this->mikron = $mikron;
        }

        /**
        * Регистрация роли в Acl
        * 
        * @param Mikron_Acl_Role $role
        */
        private function addRole($ACLRole)
        {
            $this->roles[$ACLRole->getCode()] = $ACLRole;
        }
        /**
        * Регистрация модуля в Acl
        * 
        * @param Mikron_Acl_Module $module
        */
        function AddModule($module)
        {
            $this->modules[] = $module;
        }
        /**
        * Добавляет права-разрешения
        * 
        * @param mixed $role Имя роли, для которой добавляется право, либо объект типа Mikron_Acl_Role 
        * @param mixed $module Имя модуля, для которой добавляется право, либо объект типа Mikron_Acl_Module
        * @param mixed $operation Имя операции, для которой добавляется право, либо объект типа Mikron_Acl_Operation
        */
        private function Allow($role, $module, $operation)
        {
        }
        
        /**
        * Добавляет физически роль пользователю
        * 
        * @param T_USER $user
        * @param string $code
        * @param int $days
        * @return T_USERS_ROLES
        */
        public function addRoleToUser($user, $code, $days = 9999)
        {
            if(!is_numeric($days))
            {
                throw new Exception('Невозможно добавить роль. Неверные параметры.');
            }
            global $mikron;
            $dateLimit = time() + SECONDS_PER_DAY * (int)$days;
            $dateLimit = min($dateLimit, MAX_UNIXDATE);
            $ur = new T_USERS_ROLES();
            $ur->user->value = $user->id;
            $role = $mikron->Roles->ByCode($code);
            $ur->role->value = $role->id;
            $ur->expired->value = date($mikron->Constants->DateTimeFormatMySQL, $dateLimit);
            return $ur;
        }

    }

    class Mikron_Module_Information
    {
        private $Code = null;
        private $Name = null;
        private $Description = null;
        private $Operations;
        private $Path = null;
        function __construct($code, $name, $description, $operations, $path)
        {
            if(!is_array($operations))
            {
                $operations = Array($operations);
            }
            foreach($operations as $operation)
            {
                if(!($operation instanceof Mikron_Module_Operation))
                {
                    throw new Exception('Ожидался объект типа Mikron_Module_Operation.');
                }
            }
            $this->Code = $code;
            $this->Name = $name;
            $this->Description = $description;
            $this->Path = $path;
            $this->Operations = $operations;
        }
        function Code()
        {
          return $this->Code;  
        }
        function Name()
        {
          return $this->Name;  
        }
        function Description()
        {
          return $this->Description;  
        }
        function Path()
        {
          return $this->Path;  
        }
        function Operations()
        {
          return $this->Operations;  
        }
    }

    class Mikron_Module_Operation
    {
        private $Code = null;
        private $Name = null;
        function __construct($Code, $Name)
        {
            $this->Name = $Name;
            $this->Code = $Code;
        }
        function Name()
        {
          return $this->Name;  
        }
        function Code()
        {
          return $this->Code;  
        }
    }

?>